<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>Document</title>
</head>
<body>
<!-- 先实现h5的api -->
<!-- pushState 往路由栈中增添一个路径 -->
<!-- replaceState（替换掉栈顶部的路径） -->
<button onclick="go('/a')">去a</button>
<button onclick="go('/b')">去b</button>
<button onclick="go('a',true)">替换去a</button>
<button onclick="go('b',true)">替换去b</button>

<script>
  function createCurrentLocation(base) {
    const {pathname, search, hash} = window.location
    let pos = base.indexOf('#')
    if (pos > -1) {
      return hash.slice(1) || '/'
    }
    return pathname + search + hash
  }

  function buildState(back, current, forward, replaced = false, computedScroll = false) {
    return {
      back,
      current,
      forward,
      replaced,
      computedScroll: computedScroll ? {x: window.pageXOffset, y: window.pageYOffset} : {},
      position: window.history.length - 1
    }
  }

  function useHistoryNavigation(base) {
    const {history, location} = window
    const currentLocation = {
      value: createCurrentLocation(base)
    }
    let historyState = {
      value: history.state
    }
    if (!historyState.value) {
      changeLocation(currentLocation.value, buildState(null, currentLocation.value, null, true), true)
    }

    function changeLocation(to, state, replaced = false) {
      const pos = base.indexOf('#')
      to = pos > -1 ? base + to : to
      window.history[replaced ? 'replaceState' : 'pushState'](state, null, to)
      historyState.value = state
    }

    function push(to, state) {
      const currentState = Object.assign(
        {},
        historyState.value,
        {forward: to, computedScroll: {x: window.pageXOffset, y: window.pageYOffset}}
      )
      changeLocation(currentState.current, currentState, true)

      const nextState = Object.assign({}, buildState(currentState.current, to, null))
      changeLocation(to, nextState);
      currentLocation.value = to
    }

    function replace(to, state) {
      const currentState = Object.assign(
        {},
        buildState(historyState.value.back, to, historyState.value.forward),
        state
      )
      changeLocation(to, currentState, true)
      currentLocation.value = to
    }

    return {
      location: currentLocation,
      state: historyState,
      push,
      replace
    }
  }

  function useHistoryListeners({location, state: historyState}, base) {
    let listeners = []

    const handler = function ({state}) {
      const to = createCurrentLocation(base)
      const from = location.value
      const fromState = historyState.value

      location.value = to
      historyState.value = state
      listeners.forEach(callback => callback(to, from, {}))
    }
    window.addEventListener('popstate', handler)

    function listen(callback) {
      listeners.push(callback)
    }

    return {
      listen
    }
  }

  function createWebHistory(base = '') {
    const historyNavigation = useHistoryNavigation(base);
    const historyListeners = useHistoryListeners(historyNavigation, base);
    return Object.assign({}, historyNavigation, historyListeners)
  }

  function createWebHashHistory(base = '') {
    return createWebHistory('#')
  }

  // const routerHistory = createWebHistory();
  const routerHistory = createWebHashHistory('/');

  function go(path, state, replaced = false) {
    routerHistory[replaced ? "replace" : "push"](path, state);
  }

  routerHistory.listen((to, from, options) => {
    console.log(to, from, options);
  });
</script>
</body>
</html>
